home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet internetowy / Przegladarki internetowe / Mozilla Seamonkey 1.0.5 pl / seamonkey-1.0.5.pl-PL.win32.installer.exe / BROWSER.XPI / bin / chrome / help.jar / content / help / help.js < prev    next >
Encoding:
JavaScript  |  2005-07-08  |  23.7 KB  |  712 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  * ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Mozilla Communicator client code, released
  16.  * March 31, 1998.
  17.  *
  18.  * The Initial Developer of the Original Code is
  19.  * Netscape Communications Corporation.
  20.  * Portions created by the Initial Developer are Copyright (C) 1998-1999
  21.  * the Initial Developer. All Rights Reserved.
  22.  *
  23.  * Contributor(s):
  24.  *   Ian Oeschger <oeschger@brownhen.com> (Original Author)
  25.  *   Peter Wilson (added sidebar tabs)
  26.  *   R.J. Keller <rlk@trfenv.com>
  27.  *
  28.  * Alternatively, the contents of this file may be used under the terms of
  29.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  30.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31.  * in which case the provisions of the GPL or the LGPL are applicable instead
  32.  * of those above. If you wish to allow use of your version of this file only
  33.  * under the terms of either the GPL or the LGPL, and not to allow others to
  34.  * use your version of this file under the terms of the MPL, indicate your
  35.  * decision by deleting the provisions above and replace them with the notice
  36.  * and other provisions required by the GPL or the LGPL. If you do not delete
  37.  * the provisions above, a recipient may use your version of this file under
  38.  * the terms of any one of the MPL, the GPL or the LGPL.
  39.  *
  40.  * ***** END LICENSE BLOCK ***** */
  41.  
  42. //-------- global variables
  43. var helpBrowser;
  44. var helpExternal;
  45. var helpSearchPanel;
  46. var emptySearch;
  47. var emptySearchText = "No search items found.";
  48. var emptySearchLink = "about:blank";
  49. var helpTocPanel;
  50. var helpIndexPanel;
  51. var helpGlossaryPanel;
  52.  
  53. // Namespaces
  54. const NC = "http://home.netscape.com/NC-rdf#";
  55. const MAX_LEVEL = 40; // maximum depth of recursion in search datasources.
  56.  
  57. // Resources
  58. const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);
  59. const RDF_ROOT = RDF.GetResource("urn:root");
  60. const NC_PANELLIST = RDF.GetResource(NC + "panellist");
  61. const NC_PANELID = RDF.GetResource(NC + "panelid");
  62. const NC_PLATFORM = RDF.GetResource(NC + "platform");
  63. const NC_EMPTY_SEARCH_TEXT = RDF.GetResource(NC + "emptysearchtext");
  64. const NC_EMPTY_SEARCH_LINK = RDF.GetResource(NC + "emptysearchlink");
  65. const NC_DATASOURCES = RDF.GetResource(NC + "datasources");
  66. const NC_SUBHEADINGS = RDF.GetResource(NC + "subheadings");
  67. const NC_NAME = RDF.GetResource(NC + "name");
  68. const NC_CHILD = RDF.GetResource(NC + "child");
  69. const NC_LINK = RDF.GetResource(NC + "link");
  70. const NC_TITLE = RDF.GetResource(NC + "title");
  71. const NC_BASE = RDF.GetResource(NC + "base"); 
  72. const NC_DEFAULTTOPIC = RDF.GetResource(NC + "defaulttopic"); 
  73.  
  74. const RDFCUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].getService(Components.interfaces.nsIRDFContainerUtils);
  75. var RDFContainer = Components.classes["@mozilla.org/rdf/container;1"].createInstance(Components.interfaces.nsIRDFContainer);
  76. const CONSOLE_SERVICE = Components.classes['@mozilla.org/consoleservice;1'].getService(Components.interfaces.nsIConsoleService);
  77.             
  78. var RE;
  79.  
  80. var helpFileURI;
  81. var helpFileDS;
  82. // Set from nc:base attribute on help rdf file. It may be used for prefix reduction on all links within
  83. // the current help set.
  84. var helpBaseURI;
  85.  
  86. const defaultHelpFile = "chrome://help/locale/mozillahelp.rdf";
  87. // Set from nc:defaulttopic. It is used when the requested uri has no topic specified. 
  88. var defaultTopic = "welcome"; 
  89. var searchDatasources = "rdf:null";
  90. var searchDS = null;
  91. var platform = /mac/i.test(navigator.platform) ? "mac" :
  92.                /win/i.test(navigator.platform) ? "win" :
  93.                /os\/2/i.test(navigator.platform) ? "os/2" : "unix";
  94.  
  95. const NSRESULT_RDF_SYNTAX_ERROR = 0x804e03f7; 
  96.  
  97. // This function is called by dialogs/windows that want to display context-sensitive help
  98. // These dialogs/windows should include the script chrome://help/content/contextHelp.js
  99. function displayTopic(topic) {
  100.   // Use default topic if topic is not specified.
  101.   if (!topic)
  102.     topic = defaultTopic;
  103.  
  104.   // Get the help page to open.
  105.   var uri = getLink(topic);
  106.  
  107.   // Use default topic if specified topic is not found.
  108.   if (!uri) // Topic not found - revert to default.
  109.     uri = getLink(defaultTopic);
  110.   loadURI(uri);
  111. }
  112.  
  113. var helpContentListener = {
  114.   onStartURIOpen: function(aURI) {
  115.     return false;
  116.   },
  117.   doContent: function(aContentType, aIsContentPreferred, aRequest, aContentHandler) {
  118.     throw Components.results.NS_ERROR_UNEXPECTED;
  119.   },
  120.   isPreferred: function(aContentType, aDesiredContentType) {
  121.     return false;
  122.   },
  123.   canHandleContent: function(aContentType, aIsContentPreferred, aDesiredContentType) {
  124.     return false;
  125.   },
  126.   loadCookie: null,
  127.   parentContentListener: null,
  128.   QueryInterface: function (aIID) {
  129.     if (aIID.equals(Components.interfaces.nsIURIContentListener) ||
  130.         aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
  131.         aIID.equals(Components.interfaces.nsISupports))
  132.       return this;
  133.     Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  134.     return null;
  135.   }
  136. };
  137.  
  138. // Initialize the Help window
  139. function init() {
  140.   //cache panel references.
  141.   helpSearchPanel = document.getElementById("help-search-panel");
  142.   helpTocPanel = document.getElementById("help-toc-panel");
  143.   helpIndexPanel = document.getElementById("help-index-panel");
  144.   helpGlossaryPanel = document.getElementById("help-glossary-panel");
  145.   helpBrowser = document.getElementById("help-content");
  146.   helpExternal = document.getElementById("help-external");
  147.   helpExternal.docShell.useErrorPages = false;
  148.   helpExternal
  149.     .docShell
  150.     .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  151.     .getInterface(Components.interfaces.nsIURIContentListener)
  152.     .parentContentListener = helpContentListener;
  153.  
  154.   // Get the help content pack, base URL, and help topic
  155.   var helpTopic = defaultTopic;
  156.   if ("arguments" in window && window.arguments[0] instanceof Components.interfaces.nsIDialogParamBlock) {
  157.     helpFileURI = window.arguments[0].GetString(0);
  158.     helpBaseURI = helpFileURI.substring(0, helpFileURI.lastIndexOf("/")+1); // trailing "/" included.
  159.     helpTopic = window.arguments[0].GetString(1);
  160.   }
  161.  
  162.   loadHelpRDF();
  163.  
  164.   displayTopic(helpTopic);
  165.  
  166.   window.XULBrowserWindow = new nsHelpStatusHandler();
  167.  
  168.   //Start the status handler.
  169.   window.XULBrowserWindow.init();
  170.  
  171.   // Hook up UI through Progress Listener
  172.   helpBrowser.addProgressListener(window.XULBrowserWindow, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  173.   helpExternal.addProgressListener(window.XULBrowserWindow, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  174.  
  175.   //Always show the Table of Contents sidebar at startup.
  176.   showPanel('help-toc');
  177. }
  178.  
  179. function contentClick(event) {
  180.   // is this a left click on a link?
  181.   if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey || event.button != 0)
  182.     return true;
  183.  
  184.   // is this a link?
  185.   var target = event.target;
  186.   while (!(target instanceof HTMLAnchorElement))
  187.     if (!(target = target.parentNode))
  188.       return true;
  189.  
  190.   // is this an internal link?
  191.   if (target.href.lastIndexOf("chrome:", 0) == 0)
  192.     return true;
  193.  
  194.   const loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_IS_LINK;
  195.   try {
  196.     helpExternal.webNavigation.loadURI(target.href, loadFlags, null, null, null);
  197.   } catch (e) {}
  198.   return false;
  199. }
  200.  
  201. function loadHelpRDF() {
  202.   if (!helpFileDS) {
  203.     try {
  204.       helpFileDS = RDF.GetDataSourceBlocking(helpFileURI);
  205.     }
  206.     catch (e if (e.result == NSRESULT_RDF_SYNTAX_ERROR)) {
  207.       log("Help file: " + helpFileURI + " contains a syntax error.");
  208.     }
  209.     catch (e) {
  210.       log("Help file: " + helpFileURI + " was not found.");
  211.     }
  212.     try {
  213.       document.title = getAttribute(helpFileDS, RDF_ROOT, NC_TITLE, "");
  214.       helpBaseURI = getAttribute(helpFileDS, RDF_ROOT, NC_BASE, helpBaseURI);
  215.       defaultTopic = getAttribute(helpFileDS, RDF_ROOT, NC_DEFAULTTOPIC, "welcome");
  216.  
  217.       var panelDefs = helpFileDS.GetTarget(RDF_ROOT, NC_PANELLIST, true);      
  218.       RDFContainer.Init(helpFileDS, panelDefs);
  219.       var iterator = RDFContainer.GetElements();
  220.       while (iterator.hasMoreElements()) {
  221.         var panelDef = iterator.getNext();
  222.  
  223.         var panelPlatforms = getAttribute(helpFileDS, panelDef, NC_PLATFORM, platform);
  224.         panelPlatforms = panelPlatforms.split(/\s+/);
  225.  
  226.         if (panelPlatforms.indexOf(platform) == -1)
  227.           continue; // ignore datasources for other platforms.
  228.  
  229.         var panelID = getAttribute(helpFileDS, panelDef, NC_PANELID, null);
  230.  
  231.         var datasources = getAttribute(helpFileDS, panelDef, NC_DATASOURCES, "rdf:null");
  232.         datasources = normalizeLinks(helpBaseURI, datasources);
  233.         // cache additional datsources to augment search datasources.
  234.         if (panelID == "search") {
  235.           emptySearchText = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_TEXT, emptySearchText);
  236.           emptySearchLink = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_LINK, emptySearchLink);
  237.           searchDatasources += " " + datasources;
  238.           continue; // but don't try to display them yet!
  239.         }  
  240.  
  241.         // cache toc datasources for use by ID lookup.
  242.         var tree = document.getElementById("help-" + panelID + "-panel");
  243.         loadDatabasesBlocking(datasources);
  244.         tree.setAttribute("datasources", tree.getAttribute("datasources") + " " + datasources);
  245.       }  
  246.     }
  247.     catch (e) {
  248.       log(e + "");      
  249.     }
  250.   }
  251. }
  252.  
  253. function loadDatabasesBlocking(datasources) {
  254.   var ds = datasources.split(/\s+/);
  255.   for (var i=0; i < ds.length; ++i) {
  256.     if (ds[i] == "rdf:null" || ds[i] == "")
  257.       continue;
  258.     try {  
  259.       // we need blocking here to ensure the database is loaded so getLink(topic) works.
  260.       var datasource = RDF.GetDataSourceBlocking(ds[i]);
  261.     }
  262.     catch (e) {
  263.       log("Datasource: " + ds[i] + " was not found.");
  264.     }
  265.   }
  266. }
  267.  
  268. // prepend helpBaseURI to list of space separated links if the don't start with "chrome:"
  269. function normalizeLinks(helpBaseURI, links) {
  270.   if (!helpBaseURI)
  271.     return links;
  272.   var ls = links.split(/\s+/);
  273.   if (ls.length == 0)
  274.     return links;
  275.   for (var i=0; i < ls.length; ++i) {
  276.     if (ls[i] == "")
  277.       continue;
  278.     if (ls[i].substr(0,7) != "chrome:" && ls[i].substr(0,4) != "rdf:") 
  279.       ls[i] = helpBaseURI + ls[i];
  280.   }
  281.   return ls.join(" ");  
  282. }
  283.  
  284. function getLink(ID) {
  285.   if (!ID)
  286.     return null;
  287.   // Note resources are stored in fileURL#ID format.
  288.   // We have one possible source for an ID for each datasource in the composite datasource.
  289.   // The first ID which matches is returned.
  290.   var tocTree = document.getElementById("help-toc-panel");
  291.   var tocDS = tocTree.database;
  292.     if (tocDS == null)
  293.       return null;
  294.     var tocDatasources = tocTree.getAttribute("datasources");
  295.   var ds = tocDatasources.split(/\s+/);
  296.   for (var i=0; i < ds.length; ++i) {
  297.     if (ds[i] == "rdf:null" || ds[i] == "")
  298.       continue;
  299.     try {
  300.       var rdfID = ds[i] + "#" + ID;
  301.       var resource = RDF.GetResource(rdfID);
  302.       if (resource) {
  303.         var link = tocDS.GetTarget(resource, NC_LINK, true);
  304.         if (link) {
  305.           link = link.QueryInterface(Components.interfaces.nsIRDFLiteral);
  306.           if (link) 
  307.             return link.Value;
  308.           else  
  309.             return null;
  310.         }  
  311.       }
  312.     }
  313.     catch (e) { log(rdfID + " " + e);}
  314.   }
  315.   return null;
  316. }
  317.  
  318. // Called by contextHelp.js to determine if this window is displaying the requested help file.
  319. function getHelpFileURI() {
  320.   return helpFileURI;
  321. }
  322.  
  323.  
  324. function getWebNavigation()
  325. {
  326.   return helpBrowser.webNavigation;
  327. }
  328.  
  329. function loadURI(uri)
  330. {
  331.   if (uri.substr(0,7) != "chrome:")
  332.     uri = helpBaseURI + uri;
  333.   const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  334.   getWebNavigation().loadURI(uri, nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
  335. }
  336.  
  337. function goBack()
  338. {
  339.   var webNavigation = getWebNavigation();
  340.   if (webNavigation.canGoBack)
  341.     webNavigation.goBack();
  342. }
  343.  
  344. function goForward()
  345. {
  346.   var webNavigation = getWebNavigation();
  347.   if (webNavigation.canGoForward)
  348.     webNavigation.goForward();
  349. }
  350.  
  351. function goHome() {
  352.   // load "Welcome" page
  353.   displayTopic(defaultTopic);
  354. }
  355.  
  356. function print()
  357. {
  358.   try {
  359.     content.print();
  360.   } catch (e) {
  361.   }
  362. }
  363.  
  364. function createBackMenu(event)
  365. {
  366.   return FillHistoryMenu(event.target, "back");
  367. }
  368.  
  369. function createForwardMenu(event)
  370. {
  371.   return FillHistoryMenu(event.target, "forward");
  372. }
  373.  
  374. function gotoHistoryIndex(aEvent)
  375. {
  376.   var index = aEvent.target.getAttribute("index");
  377.   if (!index)
  378.     return false;
  379.   try {
  380.     getWebNavigation().gotoIndex(index);
  381.   }
  382.   catch(ex) {
  383.     return false;
  384.   }
  385.   return true;
  386. }
  387.  
  388. function nsHelpStatusHandler()
  389. {
  390. }
  391.  
  392. nsHelpStatusHandler.prototype =
  393. {
  394.   onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
  395.   {
  396.     const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  397.  
  398.     // Turn on the throbber.
  399.     if (aStateFlags & nsIWebProgressListener.STATE_START)
  400.       this.throbberElement.setAttribute("busy", "true");
  401.     else if (aStateFlags & nsIWebProgressListener.STATE_STOP)
  402.       this.throbberElement.removeAttribute("busy");
  403.   },
  404.   onStatusChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {},
  405.   onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress,
  406.                               aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {},
  407.   onSecurityChange : function(aWebProgress, aRequest, state) {},
  408.   onLocationChange : function(aWebProgress, aRequest, aLocation)
  409.   {
  410.     UpdateBackForwardButtons();
  411.   },
  412.   QueryInterface : function(aIID)
  413.   {
  414.     if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
  415.       aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
  416.       aIID.equals(Components.interfaces.nsIXULBrowserWindow) ||
  417.       aIID.equals(Components.interfaces.nsISupports))
  418.       return this;
  419.     throw Components.results.NS_NOINTERFACE;
  420.   },
  421.  
  422.   init : function()
  423.   {
  424.     this.throbberElement = document.getElementById("navigator-throbber");
  425.   },
  426.  
  427.   destroy : function()
  428.   {
  429.     //this is needed to avoid memory leaks, see bug 60729
  430.     this.throbberElement = null;
  431.   },
  432.  
  433.   setJSStatus : function(status) {},
  434.   setJSDefaultStatus : function(status) {},
  435.   setOverLink : function(link) {}
  436. }
  437.  
  438. function UpdateBackForwardButtons()
  439. {
  440.   var backBroadcaster = document.getElementById("canGoBack");
  441.   var forwardBroadcaster = document.getElementById("canGoForward");
  442.   var webNavigation = getWebNavigation();
  443.  
  444.   // Avoid setting attributes on broadcasters if the value hasn't changed!
  445.   // Remember, guys, setting attributes on elements is expensive!  They
  446.   // get inherited into anonymous content, broadcast to other widgets, etc.!
  447.   // Don't do it if the value hasn't changed! - dwh
  448.  
  449.   var backDisabled = (backBroadcaster.getAttribute("disabled") == "true");
  450.   var forwardDisabled = (forwardBroadcaster.getAttribute("disabled") == "true");
  451.  
  452.   if (backDisabled == webNavigation.canGoBack)
  453.     backBroadcaster.setAttribute("disabled", !backDisabled);
  454.   
  455.   if (forwardDisabled == webNavigation.canGoForward)
  456.     forwardBroadcaster.setAttribute("disabled", !forwardDisabled);
  457. }
  458.  
  459. var gFindInstData;
  460. function getFindInstData()
  461. {
  462.   if (!gFindInstData) {
  463.     gFindInstData = new nsFindInstData();
  464.     gFindInstData.browser = helpBrowser;
  465.     // defaults for rootSearchWindow and currentSearchWindow are fine here
  466.   }
  467.   return gFindInstData;
  468. }
  469.  
  470. function find(again, reverse)
  471. {
  472.   if (again)
  473.     findAgainInPage(getFindInstData(), reverse);
  474.   else
  475.     findInPage(getFindInstData())
  476. }
  477.  
  478. function getMarkupDocumentViewer()
  479. {
  480.   return helpBrowser.markupDocumentViewer;
  481. }
  482.  
  483. //Show the selected sidebar panel
  484. function showPanel(panelId) {
  485.   //hide other sidebar panels and show the panel name taken in from panelID.
  486.   helpSearchPanel.setAttribute("hidden", "true");
  487.   helpTocPanel.setAttribute("hidden", "true");
  488.   helpIndexPanel.setAttribute("hidden", "true");
  489.   helpGlossaryPanel.setAttribute("hidden", "true");
  490.   var thePanel = document.getElementById(panelId + "-panel");
  491.   thePanel.setAttribute("hidden","false");
  492.  
  493.   //remove the selected style from the previous panel selected.
  494.   document.getElementById("help-glossary-btn").removeAttribute("selected");
  495.   document.getElementById("help-index-btn").removeAttribute("selected");
  496.   document.getElementById("help-search-btn").removeAttribute("selected");
  497.   document.getElementById("help-toc-btn").removeAttribute("selected");
  498.  
  499.   //add the selected style to the correct panel.
  500.   var theButton = document.getElementById(panelId + "-btn");
  501.   theButton.setAttribute("selected", "true");
  502.   document.commandDispatcher.advanceFocusIntoSubtree(theButton);
  503. }
  504.  
  505. function onselect_loadURI(tree) {
  506.   var row = tree.currentIndex;
  507.   if (row >= 0) {
  508.     var resource = tree.view.getResourceAtIndex(row);
  509.     var link = tree.database.GetTarget(resource, NC_LINK, true);
  510.     if (link instanceof Components.interfaces.nsIRDFLiteral && link.Value)
  511.       loadURI(link.Value);
  512.   }
  513. }
  514.  
  515. function doFind() {
  516.   var searchTree = document.getElementById("help-search-tree");
  517.   var findText = document.getElementById("findText");
  518.  
  519.   // clear any previous results.
  520.   clearDatabases(searchTree.database);
  521.  
  522.     // if the search string is empty or contains only whitespace, purge the results tree and return
  523.     RE = findText.value.match(/\S+/g);
  524.     if (!RE) {
  525.       searchTree.builder.rebuild();
  526.       return;
  527.     }
  528.  
  529.     // compile the search string, which has already been split up above, into regexps
  530.     for (var i=0; i < RE.length; ++i) {
  531.       RE[i] = new RegExp(RE[i], "i");
  532.   }
  533.   emptySearch = true;
  534.  
  535.   // search TOC
  536.   var resultsDS =  Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].createInstance(Components.interfaces.nsIRDFDataSource);
  537.   var tree = document.getElementById("help-toc-panel");
  538.   var sourceDS = tree.database;
  539.   doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
  540.  
  541.   // search additional search datasources
  542.   if (searchDatasources != "rdf:null") {
  543.     if (!searchDS)
  544.       searchDS = loadCompositeDS(searchDatasources);
  545.     doFindOnDatasource(resultsDS, searchDS, RDF_ROOT, 0);
  546.   }
  547.  
  548.   // search index.
  549.   tree = document.getElementById("help-index-panel");
  550.   sourceDS = tree.database;
  551.   if (!sourceDS) // If the index has never been displayed this will be null.
  552.     sourceDS = loadCompositeDS(tree.datasources);
  553.   doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
  554.  
  555.   // search glossary.
  556.   tree = document.getElementById("help-glossary-panel");
  557.   sourceDS = tree.database;
  558.   if (!sourceDS) // If the glossary has never been displayed this will be null (sigh!).
  559.     sourceDS = loadCompositeDS(tree.datasources);
  560.   doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
  561.  
  562.   if (emptySearch)
  563.         assertSearchEmpty(resultsDS);
  564.   // Add the datasource to the search tree
  565.   searchTree.database.AddDataSource(resultsDS);
  566.   searchTree.builder.rebuild();
  567. }
  568.  
  569. function clearDatabases(compositeDataSource) {
  570.   var enumDS = compositeDataSource.GetDataSources()
  571.   while (enumDS.hasMoreElements()) {
  572.     var ds = enumDS.getNext();
  573.         compositeDataSource.RemoveDataSource(ds);
  574.   }
  575. }
  576.  
  577. function doFindOnDatasource(resultsDS, sourceDS, resource, level) {
  578.   if (level > MAX_LEVEL) {
  579.     try {
  580.       log("Recursive reference to resource: " + resource.Value + ".");
  581.     }
  582.     catch (e) {
  583.       log("Recursive reference to unknown resource.");
  584.     }
  585.     return;
  586.   }
  587.   // find all SUBHEADING children of current resource.
  588.   var targets = sourceDS.GetTargets(resource, NC_SUBHEADINGS, true);
  589.   while (targets.hasMoreElements()) {
  590.       var target = targets.getNext();
  591.       target = target.QueryInterface(Components.interfaces.nsIRDFResource);
  592.         // The first child of a rdf:subheading should (must) be a rdf:seq.
  593.         // Should we test for a SEQ here?
  594.     doFindOnSeq(resultsDS, sourceDS, target, level+1);       
  595.   }  
  596. }
  597.  
  598. function doFindOnSeq(resultsDS, sourceDS, resource, level) {
  599.   // load up an RDFContainer so we can access the contents of the current rdf:seq.    
  600.   RDFContainer.Init(sourceDS, resource);
  601.   var targets = RDFContainer.GetElements();
  602.   while (targets.hasMoreElements()) {
  603.     var target = targets.getNext();
  604.     var link = sourceDS.GetTarget(target, NC_LINK, true);
  605.     var name = sourceDS.GetTarget(target, NC_NAME, true);
  606.     name = name.QueryInterface(Components.interfaces.nsIRDFLiteral);
  607.     if (link && isMatch(name.Value)) {
  608.       // we have found a search entry - add it to the results datasource.
  609.       var urn = RDF.GetAnonymousResource();
  610.       resultsDS.Assert(urn, NC_NAME, name, true);
  611.       resultsDS.Assert(urn, NC_LINK, link, true);
  612.       resultsDS.Assert(RDF_ROOT, NC_CHILD, urn, true);
  613.       emptySearch = false;     
  614.     }
  615.     // process any nested rdf:seq elements.
  616.     doFindOnDatasource(resultsDS, sourceDS, target, level+1);
  617.   }
  618. }
  619.  
  620. function assertSearchEmpty(resultsDS) {
  621.     var resSearchEmpty = RDF.GetResource("urn:emptySearch");
  622.     resultsDS.Assert(RDF_ROOT,
  623.              NC_CHILD,
  624.              resSearchEmpty,
  625.              true);
  626.     resultsDS.Assert(resSearchEmpty,
  627.              NC_NAME,
  628.              RDF.GetLiteral(emptySearchText),
  629.              true);
  630.     resultsDS.Assert(resSearchEmpty,
  631.              NC_LINK,
  632.              RDF.GetLiteral(emptySearchLink),
  633.              true);
  634. }
  635.  
  636. function isMatch(text) {
  637.   for (var i=0; i < RE.length; ++i ) {
  638.     if (!RE[i].test(text))
  639.       return false;
  640.   }
  641.   return true;
  642. }
  643.  
  644. function loadCompositeDS(datasources) {
  645.   // We can't search on each individual datasource's - only the aggregate (for each sidebar tab)
  646.   // has the appropriate structure.
  647.   var compositeDS =  Components.classes["@mozilla.org/rdf/datasource;1?name=composite-datasource"]
  648.       .createInstance(Components.interfaces.nsIRDFCompositeDataSource);
  649.   
  650.   var ds = datasources.split(/\s+/);
  651.   for (var i=0; i < ds.length; ++i) {
  652.     if (ds[i] == "rdf:null" || ds[i] == "")
  653.       continue;
  654.     try {  
  655.       // we need blocking here to ensure the database is loaded.
  656.       var sourceDS = RDF.GetDataSourceBlocking(ds[i]);
  657.       compositeDS.AddDataSource(sourceDS);
  658.     }
  659.     catch (e) {
  660.       log("Datasource: " + ds[i] + " was not found.");
  661.     }
  662.   }
  663.   return compositeDS;
  664. }
  665.  
  666. function getAttribute(datasource, resource, attributeResourceName, defaultValue) {
  667.   var literal = datasource.GetTarget(resource, attributeResourceName, true);
  668.   return literal instanceof Components.interfaces.nsIRDFLiteral ? literal.Value : defaultValue;
  669. }
  670. // Write debug string to javascript console.
  671. function log(aText) {
  672.   CONSOLE_SERVICE.logStringMessage(aText);
  673. }
  674.  
  675.  
  676. //INDEX OPENING FUNCTION -- called in oncommand for index pane
  677. // iterate over all the items in the outliner;
  678. // open the ones at the top-level (i.e., expose the headings underneath
  679. // the letters in the list.
  680. function displayIndex() {
  681.   var treeview = helpIndexPanel.view;
  682.   var i = treeview.rowCount;
  683.   while (i--)
  684.     if (!treeview.getLevel(i) && !treeview.isContainerOpen(i))
  685.       treeview.toggleOpenState(i);
  686. }
  687.  
  688. // Shows the panel relative to the currently selected panel.
  689. // Takes a boolean parameter - if true it will show the next panel, 
  690. // otherwise it will show the previous panel.
  691. function showRelativePanel(goForward) {
  692.   var selectedIndex = -1;
  693.   var sidebarBox = document.getElementById("helpsidebar-box");
  694.   var sidebarButtons = new Array();
  695.   for (var i = 0; i < sidebarBox.childNodes.length; i++) {
  696.     var btn = sidebarBox.childNodes[i];
  697.     if (btn.nodeName == "toolbarbutton") {
  698.       if (btn.getAttribute("selected") == "true")
  699.         selectedIndex = sidebarButtons.length;
  700.       sidebarButtons.push(btn);
  701.     }
  702.   }
  703.   if (selectedIndex == -1)
  704.     return;
  705.   selectedIndex += goForward ? 1 : -1;
  706.   if (selectedIndex >= sidebarButtons.length)
  707.     selectedIndex = 0;
  708.   else if (selectedIndex < 0)
  709.     selectedIndex = sidebarButtons.length - 1;
  710.   sidebarButtons[selectedIndex].doCommand();
  711. }
  712.